﻿using System;
using Microsoft.Extensions.FileProviders;
using Yaml.Extensions.Configuration;

namespace Microsoft.Extensions.Configuration
{
  public static class YamlConfigurationExtensions
  {
    /// <summary>Adds the Yaml configuration provider at <paramref name="path"/> to <paramref name="builder"/>.</summary>
    /// <param name="builder">The <see cref="IConfigurationBuilder"/> to add to.</param>
    /// <param name="path">Path relative to the base path stored in <see cref="IConfigurationBuilder.Properties"/> of <paramref name="builder"/>.</param>
    /// <returns>The <see cref="IConfigurationBuilder"/>.</returns>
    public static IConfigurationBuilder AddYamlFile(this IConfigurationBuilder builder, string path)
    {
      return AddYamlFile(builder, provider: null, path: path, optional: false, reloadOnChange: false);
    }

    /// <summary>Adds the Yaml configuration provider at <paramref name="path"/> to <paramref name="builder"/>.</summary>
    /// <param name="builder">The <see cref="IConfigurationBuilder"/> to add to.</param>
    /// <param name="path">Path relative to the base path stored in <see cref="IConfigurationBuilder.Properties"/> of <paramref name="builder"/>.</param>
    /// <param name="optional">Whether the file is optional.</param>
    /// <returns>The <see cref="IConfigurationBuilder"/>.</returns>
    public static IConfigurationBuilder AddYamlFile(this IConfigurationBuilder builder, string path, bool optional)
    {
      return AddYamlFile(builder, provider: null, path: path, optional: optional, reloadOnChange: false);
    }

    /// <summary>Adds the Yaml configuration provider at <paramref name="path"/> to <paramref name="builder"/>.</summary>
    /// <param name="builder">The <see cref="IConfigurationBuilder"/> to add to.</param>
    /// <param name="path">Path relative to the base path stored in <see cref="IConfigurationBuilder.Properties"/> of <paramref name="builder"/>.</param>
    /// <param name="optional">Whether the file is optional.</param>
    /// <param name="reloadOnChange">Whether the configuration should be reloaded if the file changes.</param>
    /// <returns>The <see cref="IConfigurationBuilder"/>.</returns>
    public static IConfigurationBuilder AddYamlFile(this IConfigurationBuilder builder, string path, bool optional, bool reloadOnChange)
    {
      return AddYamlFile(builder, provider: null, path: path, optional: optional, reloadOnChange: reloadOnChange);
    }

    /// <summary>Adds a Yaml configuration source to <paramref name="builder"/>.</summary>
    /// <param name="builder">The <see cref="IConfigurationBuilder"/> to add to.</param>
    /// <param name="provider">The <see cref="IFileProvider"/> to use to access the file.</param>
    /// <param name="path">Path relative to the base path stored in <see cref="IConfigurationBuilder.Properties"/> of <paramref name="builder"/>.</param>
    /// <param name="optional">Whether the file is optional.</param>
    /// <param name="reloadOnChange">Whether the configuration should be reloaded if the file changes.</param>
    /// <returns>The <see cref="IConfigurationBuilder"/>.</returns>
    public static IConfigurationBuilder AddYamlFile(this IConfigurationBuilder builder, IFileProvider provider, string path, bool optional, bool reloadOnChange)
    {
      if (builder == null) { throw new ArgumentNullException(nameof(builder)); }
      if (string.IsNullOrEmpty(path)) { throw new ArgumentException("InvalidFilePath", nameof(path)); }

      var s = new YamlConfigurationSource()
      {
        FileProvider = provider,
        Path = path,
        Optional = optional,
        ReloadOnChange = reloadOnChange
      };

      s.ResolveFileProvider();

      builder.Add(s);
      return builder;
    }

    public static string Get(this IConfigurationProvider provider, string key)
    {
      if (!provider.TryGet(key, out string value))
      {
        throw new InvalidOperationException("Key not found");
      }

      return value;
    }
  }
}